import datetime
import decimal
import re
import time
import math
import pymongo

from django.db.models.fields import *
from django.db.models.fields.related import *
from django.utils.translation import ugettext, ugettext_lazy as _
from django import forms


def DateField_to_python(org_func, self, value):

    if isinstance(value, (str,unicode)):
        if re.search(r'^\d{4}\.\d{1,2}\.\d{1,2}$', value):
            value = value.replace('.','-')
        if not value:
            value = None

    return org_func(self, value)

org_func = DateField.to_python
DateField.to_python = lambda *args,**kwargs: DateField_to_python(org_func,*args,**kwargs)

class MongoAutoField(AutoField):

    def get_prep_value(self, value):
        if value is None:
            return None

        if isinstance(value, (str,unicode) ):
            return pymongo.objectid.ObjectId(value)
        return value

    def to_python(self, value):
        return value

class DocumentField(Field):
    description = _("Document")
    empty_strings_allowed = False

    def get_internal_type(self):
        return "DocumentField"

    def __init__(self, doc_cls, verbose_name=None, name=None, **kwargs):

        Field.__init__(self, verbose_name, name, **kwargs)
        self.doc = doc_cls

    def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False ):

        if isinstance( value, dict ):
            return value
        else:
            return super(DocumentField, self).get_db_prep_lookup( lookup_type, value )

    def get_db_prep_value(self, value, connection, prepared=False):
        inst = value
        values = {}

        if inst is None:
            return None

        for f in inst._meta.local_fields:
            if isinstance(f, AutoField) : 
                continue
            value = getattr(inst, f.attname) or f.pre_save(inst, True) 
            value = f.clean( value, inst )
            values[ f.name ] = f.get_db_prep_save(value, connection=connection)

        return values


    def to_python(self, value):
        return value

    def validate(self, value, model_instance):

        Field.validate(self, value, model_instance)

        if value is None:
            return

        if not isinstance(value, self.doc):
            raise exceptions.ValidationError('%s field must be %s instance but %s' % (self.name, self.doc, type(value)))




class DocumentListField(DocumentField):
    description = _("DocumentList")

    def get_internal_type(self):
        return "DocumentListField"

    def __init__(self, doc_cls, verbose_name=None, name=None, **kwargs):

        super(DocumentListField, self).__init__(doc_cls, verbose_name, name, **kwargs)


    def get_db_prep_value(self, inst_list, connection, prepared=False):

        if inst_list is None:
            return None

        values_list = []
        for inst in inst_list:
            val = {}

            for f in inst._meta.fields:
                if isinstance(f, (AutoField, ForeignKey)) : 
                    continue
                value = getattr(inst, f.attname) or f.pre_save(inst, True) 
                value = f.clean( value, inst )
                val[ f.attname ] = f.get_db_prep_save(value, connection=connection)

            values_list.append(val)

        return values_list

    def get_prep_value(self, value):
        return self.to_python(value)


    def validate(self, value_list, model_instance):

        Field.validate(self, value_list, model_instance)

        if not value_list:
            return

        if not isinstance(value_list, list):
            raise exceptions.ValidationError('%s field must be %s instance but %s,%s' % (self.name, list, type(value_list), value_list))
        for value in value_list:
            if not isinstance(value, self.doc):
                raise exceptions.ValidationError('%s field item must be %s instance but %s' % (self.name, self.doc, type(value)))



class ListField(Field):
    description = _("ListField")

    def get_internal_type(self):
        return "ListField"

    def __init__(self, field, verbose_name=None, name=None, **kwargs):

        Field.__init__(self, verbose_name, name, **kwargs)
        self.field = field

    #def get_db_prep_value(self, value, connection, prepared=False):
    #    # Casts times into the format expected by the backend
    #    if not prepared:
    #        value = self.get_prep_value(value)

    #    return value

    def get_db_prep_value(self, inst_list, connection, prepared=False):

        if inst_list is None:
            return None

        f = lambda v : self.field.get_db_prep_value(v, connection=connection, prepared=prepared )
        return map(f, inst_list )


    def get_prep_value(self, value):
        return self.to_python(value)

    def to_python(self, value):
        return map(self.field.to_python, value)

    def validate(self, value_list, model_instance):

        Field.validate(self, value_list, model_instance)

        if not value_list:
            return

        if not isinstance(value_list, list):
            raise exceptions.ValidationError('%s field must be %s instance but %s,%s' % (self.name, list, type(value_list), value_list))
        for value in value_list:
            self.field.validate(value, model_instance)

    def formfield(self, **kwargs):
        
        from mongomodel.admin import ListFormField
        #return self.field.formfield(**kwargs)
        defaults = { 'form_class' : ListFormField ,
                'field': self.field 
                }
        defaults.update(kwargs)

        return super(ListField, self).formfield(**defaults)

class DictField(Field):
    description = _("DictField")

    def get_internal_type(self):
        return "DictField"

    def __init__(self, verbose_name=None, name=None, **kwargs):

        Field.__init__(self, verbose_name, name, **kwargs)

    #def get_db_prep_value(self, value, connection, prepared=False):
    #    # Casts times into the format expected by the backend
    #    if not prepared:
    #        value = self.get_prep_value(value)

    #    return value

    #def get_db_prep_value(self, inst_dict, connection, prepared=False):

    #    if inst_list is None:
    #        return None

    #    f = lambda v : self.field.get_db_prep_value(v, connection, prepared )
    #    return map(f, inst_list )


    #def get_prep_value(self, value):
    #    return map(self.field.get_prep_value, value )

    #def to_python(self, value):
    #    return map(self.field.to_python, value)

    def validate(self, value, model_instance):

        Field.validate(self, value, model_instance)

        if not value:
            return

        if not isinstance(value, dict):
            raise exceptions.ValidationError('%s field must be %s instance but %s,%s' % (self.name, dict, type(value), value))


